package com.oracle2mysql.migration;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Map;
import java.util.Properties;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;

/**
 * SQL转换引擎，负责将Oracle SQL转换为MySQL SQL
 */
public class SqlConverter {
    
    private static final Logger logger = LoggerFactory.getLogger(SqlConverter.class);
    private FunctionMapper functionMapper;
    private Properties config;
    
    public SqlConverter(FunctionMapper functionMapper, Properties config) {
        this.functionMapper = functionMapper;
        this.config = config;
    }
    
    /**
     * 将Oracle SQL转换为MySQL SQL
     */
    public String convert(String oracleSql) {
        if (oracleSql == null || oracleSql.trim().isEmpty()) {
            return oracleSql;
        }
        
        logger.debug("开始转换SQL，长度: {} 字符", oracleSql.length());
        
        // 对于大型SQL，使用超时机制防止卡住
        if (oracleSql.length() > 10000) {
            logger.info("处理大型SQL语句，使用超时保护机制");
            return convertWithTimeout(oracleSql);
        }
        
        String mysqlSql = oracleSql;
        
        // 1. 转换函数调用
        mysqlSql = convertFunctions(mysqlSql);
        
        // 2. 转换分页语法 (ROWNUM -> LIMIT)
        mysqlSql = convertPagination(mysqlSql);
        
        // 3. 转换数据类型
        mysqlSql = convertDataTypes(mysqlSql);
        
        // 4. 转换特殊语法
        mysqlSql = convertSpecialSyntax(mysqlSql);
        
        logger.debug("SQL转换完成\n原SQL: {}\n转换后: {}", oracleSql, mysqlSql);
        
        return mysqlSql;
    }
    
    /**
     * 使用超时机制进行SQL转换，防止处理大型SQL时卡住
     */
    private String convertWithTimeout(String oracleSql) {
        ExecutorService executor = Executors.newSingleThreadExecutor();
        Future<String> future = executor.submit(() -> {
            String mysqlSql = oracleSql;
            
            // 对所有大型SQL都使用分块处理，而不仅仅是>50000字符的
            mysqlSql = convertLargeSql(oracleSql);
            
            return mysqlSql;
        });
        
        try {
            // 增加超时时间，根据SQL长度动态调整
            long timeoutSeconds = Math.max(30, (long)(oracleSql.length() / 10000));
            logger.info("设置SQL转换超时时间: {}秒", timeoutSeconds);
            return future.get(timeoutSeconds, TimeUnit.SECONDS);
        } catch (TimeoutException e) {
            logger.error("SQL转换超时，可能包含复杂语法导致处理时间过长");
            future.cancel(true);
            // 不再直接抛出异常，而是尝试返回原始SQL，让用户知道需要手动处理
            logger.warn("返回原始SQL，请手动检查和处理");
            return oracleSql + " /* 警告：此SQL转换超时，请手动检查和处理 */";
        } catch (Exception e) {
            logger.error("SQL转换异常: {}", e.getMessage());
            // 同样尝试返回带有错误信息的原始SQL
            return oracleSql + " /* 错误：" + e.getMessage() + " */";
        } finally {
            executor.shutdown();
        }
    }
    
    /**
     * 分块处理大型SQL语句
     */
    private String convertLargeSql(String sql) {
        logger.info("分块处理大型SQL，长度: {} 字符", sql.length());
        
        // 优化分块策略，确保SQL语句的完整性
        StringBuilder result = new StringBuilder();
        
        // 对于不同大小的SQL使用不同的块大小
        int chunkSize = sql.length() > 100000 ? 20000 : 10000;
        
        int totalChunks = (int) Math.ceil((double) sql.length() / chunkSize);
        int currentChunk = 0;
        
        for (int i = 0; i < sql.length(); i += chunkSize) {
            currentChunk++;
            int end = Math.min(i + chunkSize, sql.length());
            
            // 首先尝试找到语句结束符
            int lastSemicolon = sql.lastIndexOf(';', end);
            int lastEndOfStatement = lastSemicolon;
            
            // 如果找不到分号，尝试寻找其他语句结束标记
            if (lastSemicolon <= i) {
                // 尝试找最近的右括号和分号组合
                int lastClosingParenthesis = sql.lastIndexOf(')', end);
                if (lastClosingParenthesis > i) {
                    lastEndOfStatement = lastClosingParenthesis + 1;
                }
            }
            
            // 如果找到了合适的语句结束位置，更新end
            if (lastEndOfStatement > i) {
                end = Math.min(lastEndOfStatement + 1, sql.length());
            }
            
            String chunk = sql.substring(i, end);
            logger.debug("处理块 {}/{}，大小: {} 字符", currentChunk, totalChunks, chunk.length());
            
            try {
                // 对每块进行转换
                String convertedChunk = convertFunctions(chunk);
                convertedChunk = convertPagination(convertedChunk);
                convertedChunk = convertDataTypes(convertedChunk);
                convertedChunk = convertSpecialSyntax(convertedChunk);
                
                result.append(convertedChunk);
            } catch (Exception e) {
                // 如果块转换失败，记录错误并保留原始块内容
                logger.error("块转换失败: {}", e.getMessage());
                result.append(chunk).append(" /* 块转换错误: " + e.getMessage() + " */");
            }
            
            i = end - 1; // 调整索引位置
        }
        
        logger.info("大型SQL分块处理完成");
        return result.toString();
    }
    
    /**
     * 转换函数调用
     */
    private String convertFunctions(String sql) {
        String result = sql;

        // 首先处理DECODE函数，将其转换为CASE表达式
        result = decodeToCase(result);

        // 然后处理特殊函数（使用硬编码的方式处理需要特殊逻辑的函数）
        // NVL(expr1, expr2) -> IFNULL(expr1, expr2)
        // 使用更宽松的正则表达式匹配各种参数类型
        result = result.replaceAll("(?i)NVL\\(\\s*([^,]+?)\\s*,\\s*([^)]+?)\\s*\\)", "IFNULL($1, $2)");
        
        // DECODE函数转换为CASE表达式
        // DECODE(expr, search1, result1, search2, result2, ..., default) -> 
        // CASE expr WHEN search1 THEN result1 WHEN search2 THEN result2 ... ELSE default END
        // 注意：这是一个简化实现，可能需要根据实际情况进行调整
        // 已经在前面调用了decodeToCase方法
        // result = decodeToCase(result);

        // NVL2(expr1, expr2, expr3) -> IF(expr1 IS NOT NULL, expr2, expr3)
        result = result.replaceAll("(?i)NVL2\\(\\s*([^,]+?)\\s*,\\s*([^,]+?)\\s*,\\s*([^)]+?)\\s*\\)", "IF($1 IS NOT NULL, $2, $3)");

        // TO_CHAR(date) -> CAST(date AS CHAR)
        result = result.replaceAll("(?i)TO_CHAR\\(\\s*([^)]+?)\\s*\\)", "CAST($1 AS CHAR)");

        // TO_DATE(string, format) -> STR_TO_DATE(string, format)
        result = result.replaceAll("(?i)TO_DATE\\(\\s*('.*?'|\".*?\")\\s*,\\s*('.*?'|\".*?\")\\s*\\)", "STR_TO_DATE($1, $2)");

        // SYSDATE -> NOW()
        result = result.replaceAll("(?i)SYSDATE", "NOW()");

        // CURRENT_DATE -> CURDATE()
        result = result.replaceAll("(?i)CURRENT_DATE", "CURDATE()");

        // LISTAGG/WM_CONCAT -> GROUP_CONCAT
        result = result.replaceAll("(?i)(LISTAGG|WM_CONCAT)\\(\\s*([^,]+?)\\s*,\\s*('.*?'|\".*?\")\\s*\\)", "GROUP_CONCAT($2 SEPARATOR $3)");
        result = result.replaceAll("(?i)(LISTAGG|WM_CONCAT)\\(\\s*([^)]+?)\\s*\\)", "GROUP_CONCAT($2)");

        // 然后处理从映射表中加载的其他函数（除了已在上面特殊处理的函数）
        Map<String, String> functionMap = getFunctionMap();
        String[] specialFunctions = {"NVL", "NVL2", "TO_CHAR", "TO_DATE", "SYSDATE", "CURRENT_DATE", "ROWNUM", "LISTAGG", "WM_CONCAT"};

        for (String oracleFunc : functionMap.keySet()) {
            // 跳过类型映射
            if (oracleFunc.startsWith("TYPE:")) {
                continue;
            }

            // 跳过已特殊处理的函数
            boolean isSpecial = false;
            for (String special : specialFunctions) {
                if (special.equals(oracleFunc)) {
                    isSpecial = true;
                    break;
                }
            }
            if (isSpecial) {
                continue;
            }

            // DECODE函数已在前面的decodeToCase方法中特殊处理
            if ("DECODE".equals(oracleFunc)) {
                continue;
            }

            String mysqlFunc = functionMap.get(oracleFunc);

            // 普通函数替换，避免替换到关键字
        try {
            // 使用更高效的正则表达式模式
            String regex = "(?i)(?<![A-Za-z0-9_])" + Pattern.quote(oracleFunc) + "(?![A-Za-z0-9_])";
            // 预编译正则表达式以提高性能
            Pattern pattern = Pattern.compile(regex);
            Matcher matcher = pattern.matcher(result);
            result = matcher.replaceAll(mysqlFunc);
        } catch (Exception e) {
            logger.warn("函数替换失败 {}: {}", oracleFunc, e.getMessage());
        }
        }

        return result;
    }
    
    /**
     * 将DECODE函数转换为CASE表达式
     * 由于DECODE函数的复杂性，使用专门的方法处理
     */
    private String decodeToCase(String sql) {
        logger.debug("进入decodeToCase方法，处理SQL: {}", sql);
        String result = sql;
        
        // 先检查是否包含DECODE函数
        if (!result.toUpperCase().contains("DECODE(")) {
            logger.debug("decodeToCase: SQL中不包含DECODE函数");
            return result;
        }
        
        // 逐个字符扫描查找DECODE函数调用
        int pos = 0;
        while ((pos = result.toUpperCase().indexOf("DECODE(", pos)) != -1) {
            logger.debug("decodeToCase: 找到DECODE函数开始位置: {}", pos);
            
            // 找到匹配的结束括号位置，处理嵌套括号
            int openParentheses = 1;
            int endPos = pos + 7; // 跳过 "DECODE("
            
            while (endPos < result.length() && openParentheses > 0) {
                char c = result.charAt(endPos);
                if (c == '(') {
                    openParentheses++;
                } else if (c == ')') {
                    openParentheses--;
                }
                endPos++;
            }
            
            if (openParentheses == 0) {
                String fullMatch = result.substring(pos, endPos);
                logger.debug("decodeToCase: 匹配到完整的DECODE函数: {}", fullMatch);
                
                try {
                    // 提取括号内的所有内容
                    int start = fullMatch.indexOf('(') + 1;
                    int end = fullMatch.lastIndexOf(')');
                    
                    if (start > 0 && end > start) {
                        String innerContent = fullMatch.substring(start, end).trim();
                        
                        // 解析参数列表（使用splitParams方法处理嵌套括号）
                        String[] paramParts = splitParams(innerContent);
                        
                        if (paramParts.length >= 2) {
                            // 第一个参数是表达式
                            String expr = paramParts[0].trim();
                            
                            StringBuilder caseBuilder = new StringBuilder("CASE " + expr);
                            
                            // 处理搜索值和结果对
                            int i = 1;
                            for (; i < paramParts.length - 1; i += 2) {
                                String search = paramParts[i].trim();
                                String replacement = paramParts[i + 1].trim();
                                caseBuilder.append(" WHEN " + search + " THEN " + replacement);
                            }
                            
                            // 处理默认值（如果有奇数个参数）
                            if (i < paramParts.length) {
                                caseBuilder.append(" ELSE " + paramParts[i].trim());
                            }
                            
                            caseBuilder.append(" END");
                            
                            logger.debug("decodeToCase: 转换后的CASE表达式: {}", caseBuilder.toString());
                            
                            // 替换原DECODE函数
                            result = result.substring(0, pos) + caseBuilder.toString() + result.substring(endPos);
                            
                            // 更新pos以继续搜索
                            pos += caseBuilder.length();
                        } else {
                            logger.debug("decodeToCase: 参数不足，无法转换");
                            pos += fullMatch.length();
                        }
                    } else {
                        pos += 7; // 移动到DECODE(后面继续搜索
                    }
                } catch (Exception e) {
                    logger.error("decodeToCase: 转换DECODE函数时出错: {}", e.getMessage());
                    pos += 7; // 移动到DECODE(后面继续搜索
                }
            } else {
                // 没有找到匹配的结束括号，移动到下一个位置继续搜索
                pos += 7;
            }
        }
        
        logger.debug("decodeToCase: 转换完成，结果SQL: {}", result);
        return result;
    }
    
    /**
     * 用于测试的public方法，直接调用decodeToCase方法
     */
    public String testDecodeToCaseDirect(String sql) {
        System.out.println("直接调用decodeToCase方法");
        System.out.println("输入SQL: " + sql);
        String result = decodeToCase(sql);
        System.out.println("输出SQL: " + result);
        return result;
    }

    /**
     * 分割函数参数，考虑嵌套括号的情况
     */
    private String[] splitParams(String params) {
        java.util.List<String> paramList = new java.util.ArrayList<>();
        int startIndex = 0;
        int bracketCount = 0;
        
        for (int i = 0; i < params.length(); i++) {
            char c = params.charAt(i);
            if (c == '(') {
                bracketCount++;
            } else if (c == ')') {
                bracketCount--;
            } else if (c == ',' && bracketCount == 0) {
                paramList.add(params.substring(startIndex, i).trim());
                startIndex = i + 1;
            }
        }
        
        // 添加最后一个参数
        if (startIndex < params.length()) {
            paramList.add(params.substring(startIndex).trim());
        }
        
        return paramList.toArray(new String[0]);
    }
    
    /**
     * 转换分页语法
     */
    private String convertPagination(String sql) {
        String result = sql;
        
        // 处理ROWNUM分页
        Pattern rownumPattern = Pattern.compile("(?i)WHERE\\s+ROWNUM\\s*<=\\s*(\\d+)");
        Matcher rownumMatcher = rownumPattern.matcher(result);
        
        if (rownumMatcher.find()) {
            String limit = rownumMatcher.group(1);
            result = rownumMatcher.replaceFirst("");
            result = result.trim();
            if (result.toLowerCase().endsWith("where")) {
                result = result.substring(0, result.length() - 5).trim();
            }
            result += " LIMIT " + limit;
        }
        
        // 处理OFFSET分页（ROWNUM BETWEEN ... AND ...）
        Pattern offsetPattern = Pattern.compile("(?i)WHERE\\s+ROWNUM\\s+BETWEEN\\s+(\\d+)\\s+AND\\s+(\\d+)");
        Matcher offsetMatcher = offsetPattern.matcher(result);
        
        if (offsetMatcher.find()) {
            int start = Integer.parseInt(offsetMatcher.group(1));
            int end = Integer.parseInt(offsetMatcher.group(2));
            int offset = start - 1;
            int limit = end - start + 1;
            
            result = offsetMatcher.replaceFirst("");
            result = result.trim();
            if (result.toLowerCase().endsWith("where")) {
                result = result.substring(0, result.length() - 5).trim();
            }
            result += " LIMIT " + limit + " OFFSET " + offset;
        }
        
        return result;
    }
    
    /**
     * 转换数据类型
     */
    private String convertDataTypes(String sql) {
        String result = sql;

        // 如果JSON中没有定义，则使用默认转换
        // 处理NUMBER类型，保留精度
        // 先处理带有两个参数的情况，如NUMBER(10,2)
        result = result.replaceAll("(?i)NUMBER\\s*\\(\\s*(\\d+)\\s*,\\s*(\\d+)\\s*\\)", "DECIMAL($1,$2)");
        // 再处理带有一个参数的情况，如NUMBER(10)
        result = result.replaceAll("(?i)NUMBER\\s*\\(\\s*(\\d+)\\s*\\)", "DECIMAL($1)");
        result = result.replaceAll("(?i)(?<![A-Za-z0-9_])NUMBER(?![A-Za-z0-9_])", "DECIMAL");

        // 处理VARCHAR2类型，保留长度
        result = result.replaceAll("(?i)VARCHAR2\\s*\\(\\s*(\\d+)\\s*\\)", "VARCHAR($1)");
        result = result.replaceAll("(?i)(?<![A-Za-z0-9_])VARCHAR2(?![A-Za-z0-9_])", "VARCHAR");

        result = result.replaceAll("(?i)(?<![A-Za-z0-9_])DATE(?![A-Za-z0-9_])", "DATETIME");
        result = result.replaceAll("(?i)(?<![A-Za-z0-9_])CLOB(?![A-Za-z0-9_])", "TEXT");
        result = result.replaceAll("(?i)(?<![A-Za-z0-9_])BLOB(?![A-Za-z0-9_])", "BLOB");
        
        // 处理SYS.ODCIVARCHAR2LIST类型（Oracle集合类型）
        // 在MySQL中没有直接对应，使用JSON_ARRAY并添加块注释说明
        result = result.replaceAll("(?i)(?<![A-Za-z0-9_])SYS\\.ODCIVARCHAR2LIST(?![A-Za-z0-9_])", "JSON_ARRAY /* MySQL不直接支持SYS.ODCIVARCHAR2LIST，建议使用JSON数组或其他集合实现方式 */");

        return result;
    }
    
    /**
     * 转换特殊语法
     */
    private String convertSpecialSyntax(String sql) {
        String result = sql;
        
        // 处理CONNECT BY（MySQL没有直接对应，需要使用递归CTE）
        result = result.replaceAll("(?i)CONNECT\\s+BY", "/* MySQL不支持CONNECT BY，需要使用递归CTE */");
        
        // 处理START WITH
        result = result.replaceAll("(?i)START\\s+WITH", "/* MySQL不支持START WITH，需要使用递归CTE */");
        
        // 处理LEVEL关键字，使用单词边界匹配确保只匹配单独的LEVEL关键字
        result = result.replaceAll("(?i)(?<![A-Za-z0-9_])LEVEL(?![A-Za-z0-9_])", "/* MySQL不支持LEVEL，需要在递归CTE中实现 */");
        
        // 处理ROWNID
        result = result.replaceAll("(?i)ROWNID", "/* MySQL不支持ROWNID */");
        
        return result;
    }
    
    
    
    /**
     * 获取函数映射表
     */
    private Map<String, String> getFunctionMap() {
        return functionMapper.getFunctionMap();
    }
}